/**
 * 
 */
package utils;

import java.io.*;
import java.util.*;

/**
 * @author Yoni Seroussi
 * @author Roei Raviv
 *
 */
public class MatrixLoader {
    
    private static final String ORDER = "ORDER";
    private static final String NUM_OF_MATRICES = "NUM_OF_MATRICES";
    private static final String MAT = "MAT_";
    private static final String DEFAULT_MATRICES_FILE_PATH = "matrix.txt";
    private ArrayList<double[][]> _matrices;
    private static String _pathToFile;
    
    private static Properties _props;
    private static boolean _initialized;
    private static MatrixLoader _instance; // the singleton instance.
    /**
     * Private C'tor
     */
    private MatrixLoader() {
        _matrices = new ArrayList<double[][]>();
    }
    
    /**
     * @param pathToConfFile full path to the configuration path
     */
    public static void init(String pathToFile) {
        if (_initialized) {
            return;
        }
        _instance = new MatrixLoader();
        if (pathToFile == null || pathToFile.isEmpty()) {
            _pathToFile = DEFAULT_MATRICES_FILE_PATH;
        } else {
            _pathToFile = pathToFile;
        }
        loadProperies();
        _instance.fillMatrix();
        _initialized = true;
    }
    
    /**
     * @return the instance if it is initialized.
     */
    public static final MatrixLoader getInstance() {
        if (_initialized) {
            return  _instance;
        }
        throw new IllegalStateException("Trying to access MatrixLoader " +
                "before it was initialized");
    }

    /**
    * @throws Exception
    */
   private static void loadProperies() {
       _props = new Properties();
       File inputFile = new File(_pathToFile);
       if (!inputFile.exists()) {
           if (_pathToFile.equals(DEFAULT_MATRICES_FILE_PATH)) {
               // our default configuration file is not available and
               // given properties path is not vaild - exit
               throw new IllegalArgumentException("Default matrices file: "
                       + DEFAULT_MATRICES_FILE_PATH +" does not exist, check the "
                       + "application folder for it, or supply path to "
                       + " the configuration file from command line.");
           } else {
               inputFile = new File(DEFAULT_MATRICES_FILE_PATH);
           }
       }
       FileInputStream fis = null;
       try {
           fis = new FileInputStream(inputFile);
       } catch (FileNotFoundException e) {
           // file exists if we're here 
       }
       try {
           _props.load(fis);
       } catch (Exception e) {
           throw new IllegalStateException("Failed loading properties from " + 
                   inputFile.getAbsolutePath() + " check file path or use" +
                           " default conf file: " + DEFAULT_MATRICES_FILE_PATH);
       }
       
   }
   
   public final ArrayList<double[][]> getMatrices() {
       return _matrices;
   }
   
   public void printMatricies() {
       for (double[][] mat : _matrices) {
           for (int i = 0 ; i < getOrder(); i++) {
               for (int j = 0 ; j < getOrder() ; j++) {
                   System.out.print(mat[i][j] + " ");
               }
               System.out.println();
           }
           
           System.out.println("\n");
       }
   }
   
   /**
    * @return
    */
   private int getNumOfMatrices() {
       return getIntFromProps(NUM_OF_MATRICES); 
   }
   
   
   public final int getOrder() {
       return getIntFromProps(ORDER);
   }
   
   private void fillMatrix() {
       String currentMatInput;
       int order = getOrder();
       int numOfMatrices= getNumOfMatrices();
       for (int matIdx = 1 ; matIdx < numOfMatrices; matIdx++) {
           currentMatInput = getStringFromProps(MAT + matIdx, false);
           
           if (currentMatInput == null) continue;
           
           double[][] mat = new double[order][order];
           Scanner s = new Scanner(currentMatInput);
           for (int  i = 0 ; i < order ; i++) {
               for (int j = 0 ; j < order ; j++) {
                   try {
                       mat[i][j] = Double.parseDouble(s.next().replace(',', ' ').trim());
                   } catch (Exception e) {
                       mat[i][j] = 0.0; // for default, XXX for now...
                   }
               }
           }
           _matrices.add(mat);
       }
   }

/**
    * @param key
    * @return Matching integer value for the given key.
    */
   private int getIntFromProps(String key) {
       try {
           int ret = Integer.parseInt(getStringFromProps(key, true));
           return ret;
       } catch (NumberFormatException e) {
           throw new IllegalArgumentException("Key " + key + " is not "
                   + " a valid Integer value, in configuration file: "
                   + _pathToFile);            
       }
   }
   
   /**
    * @param key
    * @return Matching value for the given key.
    */
   private String getStringFromProps(String key, boolean throwIfNotFound) {
       String ret = _props.getProperty(key);
       if (ret != null) {
           ret = ret.trim();
           if (ret.length() != 0) {
               return ret;
           }
       }
       // string was only white spaces or null
       if (throwIfNotFound) {
           throw new IllegalArgumentException("Key " + key + " was not "
                   + " found, or had no value, in configuration file: "
                   + _pathToFile);
       }
       return null;
   }
   
   public void destroy() {
       if (_initialized) {
           _props.clear();
           _props = null;
           _instance = null;
           _initialized = false;
       }
       return;
   }
   
   /**
    * @return the full path to the loaded configuration file 
    */
   public final String getConfFilePath() {
       return _pathToFile;
   }
   
}

